package com.xinxin.socket.decoder;

import com.xinxin.socket.msg.NetMsg;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;

import java.util.List;

/**
 * @author ys
 * @date 2020/8/6
 */
public final class NetMsgDecoder extends ReplayingDecoder<NetMsgDecoder.ProtoDecodePhase> {

    enum ProtoDecodePhase {
        READ_HEAD_LENGTH,
        READ_CLIENT_VERSION,
        READ_CMD_ID,
        READ_SEQ,
        READ_BODY_LENGTH,
        READ_OPTIONS,
        READ_BODY
    }

    private NetMsg msg;

    public NetMsgDecoder() {
        super(ProtoDecodePhase.READ_HEAD_LENGTH);
    }

    private ByteBuf slicePacket(ByteBuf in, int length) {
        ByteBuf slice = in.slice(in.readerIndex(), length);
        in.readerIndex(in.readerIndex() + length);
        return slice.retain();
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        if (msg == null) {
            msg = new NetMsg();
        }

        switch (state()) {
            case READ_HEAD_LENGTH:
                msg.setHeadLength(in.readInt());
                checkpoint(ProtoDecodePhase.READ_CLIENT_VERSION);
            case READ_CLIENT_VERSION:
                msg.setClientVersion(in.readInt());
                checkpoint(ProtoDecodePhase.READ_CMD_ID);
            case READ_CMD_ID:
                msg.setCmdId(in.readInt());
                checkpoint(ProtoDecodePhase.READ_SEQ);
            case READ_SEQ:
                msg.setSeq(in.readInt());
                checkpoint(ProtoDecodePhase.READ_BODY_LENGTH);
            case READ_BODY_LENGTH:
                msg.setBodyLen(in.readInt());
                checkpoint(ProtoDecodePhase.READ_OPTIONS);
            case READ_OPTIONS:
                if (msg.getOptionLen() > 0) {
                    msg.setOption(slicePacket(in, msg.getOptionLen()));
                }
                checkpoint(ProtoDecodePhase.READ_BODY);
            case READ_BODY:
                if (msg.getBodyLen() > 0) {
                    msg.setBody(slicePacket(in, msg.getBodyLen()));
                }
                out.add(msg);
                msg = null;
                checkpoint(ProtoDecodePhase.READ_HEAD_LENGTH);
                break;
            default:
                throw new IllegalStateException("proto decode state: " + state());
        }
    }
}
